using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Perovich.GameObjects.Interfaces;

namespace Perovich.GameObjects
{

    /// <summary>
    /// An abstract <see cref="DrawableComponent"/> that implements <see cref="ICanHoldComponents"/>.
    /// </summary>
    /// <remarks>
    ///     The <see cref="GameObject"/> class is a <see cref="DrawableComponent"/> that implements <see cref="ICanHoldComponents"/>.
    ///     This allows for parent-child relationships between components. Using this large groups of components can be disabled or enabled at once.
    /// </remarks>
    public abstract class GameObject : DrawableComponent, Interfaces.ICanHoldComponents
    {

        /// <summary>
        /// Gets the <see cref="ContentManager"/> for this <see cref="GameObject"/> instance.
        /// </summary>
        public ContentManager Content
        {
            get;
            private set;
        }

        private ComponentCollection _components = new ComponentCollection();

        /// <summary>
        /// Gets the <see cref="ComponentCollection"/> containing all of this <see cref="GameObject"/>s children.
        /// </summary>
        public ComponentCollection Components
        {
            get { return _components; }
        }

        /// <summary>
        /// Gets the parent of this <see cref="GameObject"/>
        /// </summary>
        public ICanHoldComponents Parent
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the <see cref="ComponentCollection"/> containing all of this <see cref="GameObject"/>s siblings.
        /// </summary>
        public ComponentCollection Siblings
        {
            get
            {
                return Parent.Components;
            }
        }

        /// <summary>Initializes a new <see cref="GameObject"/>.</summary>
        /// <param name="game">
        ///   <see cref="Perovich.GameObjects.Interfaces.ICanHoldComponents"/> instance that this <see cref="GameObject"/> will be constructed for.
        /// </param>
        protected GameObject(ICanHoldComponents game)
            : base(game)
        {
            if (game == null)
                throw new ArgumentNullException("game");
            game.Components.Add(this);
            Parent = game;
            Content = game.Content;
            game.SpriteBatchSet += new EventHandler<EventArgs>(game_SpriteBatchSet);
            SBatches = game.SBatches;
            Scale = 1;
        }


        void game_SpriteBatchSet(object sender, EventArgs e)
        {
            SBatches = (sender as Interfaces.ICanHoldComponents).SBatches;
        }

        /// <summary>Gives the <see cref="GameObject"/> a chance to initialize itself</summary>
        public override void Initialize()
        {
            Components.Initialize();
            base.Initialize();
        }

        /// <summary>Called when the <see cref="GameObject"/> needs to update itself.</summary>
        /// <param name="gameTime">Provides a snapshot of the game's timing values</param>
        public override void Update(GameTime gameTime)
        {
            if (Enabled)
            {
                base.Update(gameTime);
                Components.Update(gameTime);
                Perovich.GameObjects.ComponentsExtensions.CheckColisions(this);
            }
        }

        /// <summary>
        /// Allows the game and its children to draw on any render targets.
        /// </summary>
        /// <param name="gameTime">A snapshot of ths game's timing values.</param>
        public override void DrawOnTargets(GameTime gameTime)
        {
            if (Visible)
            {
                Components.DrawOnTargets(gameTime);
                base.DrawOnTargets(gameTime);
            }
        }

        /// <summary>Called when the <see cref="GameObject"/> needs to draw itself.</summary>
        /// <param name="gameTime">Provides a snapshot of the game's timing values.</param>
        public override void Draw(GameTime gameTime)
        {
            if (Visible)
            {
                Components.Draw(gameTime, GraphicsDevice, SBatches);
                base.Draw(gameTime);
            }
        }

        /// <summary>
        /// Releases the unmanaged resources used by the <see cref="GameObject"/> and optionally releases the managed resources.
        /// </summary>
        /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
        /// <remarks>This function also disposes all of the <see cref="GameObject"/> instances children.</remarks>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                Components.Dispose();
            }
            base.Dispose(disposing);
        }

        /// <summary>
        /// Gets the service container this <see cref="GameObject"/> was constructed for.
        /// </summary>
        public IServiceProvider Services
        {
            get { return base.GameServices; }
        }


        /// <summary>
        /// Gets or sets the Id associated with this <see cref="GameObject"/>.
        /// </summary>
        public ValueType Id
        {
            get;
            set;
        }



        private float _depth;

        /// <summary>
        /// Gets or sets the depth this <see cref="GameObject"/> is drawn at.
        /// </summary>
        /// <remarks>A <see cref="float"/> between 0 and 1.</remarks>
        public float Depth
        {
            get
            {
                return _depth;
            }
            set
            {
                float old = _depth;
                _depth = value;
                if ((DepthChanged != null) && (_depth != old))
                    DepthChanged(this, null);
            }
        }

        /// <summary>
        /// Triggered when the <see cref="GameObject.Depth"/> property is changed.
        /// </summary>
        public event EventHandler DepthChanged;

        private float _scale;

        /// <summary>
        /// Gets or sets the scale this <see cref="GameObject"/> is drawn at.
        /// </summary>
        public float Scale
        {
            get { return _scale; }
            set
            {
                float old = _scale;
                _scale = value;
                if (_scale != old)
                    SignalScaleChanged();
            }
        }

        /// <summary>
        /// Triggered when the <see cref="GameObject.Scale"/> property is changed.
        /// </summary>
        public event EventHandler ScaleChanged;

        void SignalScaleChanged()
        {
            if (ScaleChanged != null)
            {
                ScaleChanged(this, null);
            }
        }

        


        /// <summary>
        /// Gets or sets if this <see cref="GameObject"/> updates and draws itself.
        /// </summary>
        public bool Active
        {
            get
            {
                return Enabled && Visible;
            }
            set
            {
                bool old = Active;
                Enabled = value;
                Visible = value;
                if (Active != old)
                    SignalActiveChanged();
            }
        }

        /// <summary>
        /// Triggered when the <see cref="GameObject.Active"/> property is changed.
        /// </summary>
        public event EventHandler ActiveChanged;


        void SignalActiveChanged()
        {
            if (ActiveChanged != null)
            {
                ActiveChanged(this, null);
            }
        }
    }
}
